﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  DeleteInvalidRemindersTask.cs
//    
// Abstract:
//
//  This module implements the DeleteInvalidRemindersTask class. This handles the task of deleting invalid rule-based 
//  reminders via EWS.
//
//--

using System;
using System.Collections.Generic;
using System.Diagnostics;

using Microsoft.OfficeCommunicationsServer.Applications.Common;

namespace Microsoft.EnhancedReminders.Scheduler
{
    /// <summary>
    /// This handles the task of deleting invalid rule-based reminders via EWS.
    /// </summary>
    internal class DeleteInvalidRemindersTask : ExchangeTask
    {
        private readonly NotificationRules _rules;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="taskContext">Task context</param>
        /// <param name="client">WCF proxy for communicating with EWS</param>
        /// <param name="impersonation">Exchange impersonation info</param>
        /// <param name="emailAddress">E-mail address of the user associated with this request</param>
        /// <param name="rules">Notification rules</param>
        internal DeleteInvalidRemindersTask(TaskContext taskContext, ExchangeServicePortType client, ExchangeImpersonationType impersonation, string emailAddress, NotificationRules rules)
            : base(taskContext, client, impersonation, emailAddress)
        {
            _rules = rules;
        }

        /// <summary>
        /// Request the items that have invalid rules-based reminders.
        /// </summary>
        internal override void Begin()
        {
            Logger.WriteTraceVerbose(Context, "DeleteInvalidRemindersTask.Begin");

            FindItemTask findItemTask = new FindItemTask(_taskContext, _client, _impersonation, _emailAddress, 
                GetInvalidRulesSearchExpression(), 
                ExchangeHelpers.GetDistinguishedFolder(DistinguishedFolderIdNameType.calendar, _emailAddress));
            findItemTask.Completed += FindItemTask_Completed;
            findItemTask.Begin();
        }

        /// <summary>
        /// Handles completion of the task to find the items with invalid rules-based reminders.
        /// </summary>
        /// <param name="sender">Find item task</param>
        /// <param name="e">Find item event args</param>
        private void FindItemTask_Completed(object sender, TaskCompletedEventArgs e)
        {
            Debug.Assert(sender != null, "sender != null");
            Debug.Assert(e != null, "e != null");
            Debug.Assert(e.Context == Context, "e.Context == Context");

            Logger.WriteTraceVerbose(Context, "DeleteInvalidRemindersTask.FindItemTask_Completed");

            sender.AssertCast<FindItemTask>().Completed -= FindItemTask_Completed;

            if (e.Error != null)
            {
                RaiseCompleted(e);
                return;
            }

            FindItemTaskCompletedEventArgs findItemArgs = e.AssertCast<FindItemTaskCompletedEventArgs>();

            if (findItemArgs.Items == null)
            {
                Logger.WriteTraceInformation(Context, "There are no invalid reminders to delete.");
                RaiseCompleted(e);
                return;
            }

            DeleteRemindersTask deleteRemindersTask = new DeleteRemindersTask(_taskContext, _client, _impersonation, _emailAddress, findItemArgs.Items);
            deleteRemindersTask.Completed += DeleteRemindersTask_Completed;
            deleteRemindersTask.Begin();
        }

        /// <summary>
        /// Handles completion of the task to delete the invalid rules-based reminders.
        /// </summary>
        /// <param name="sender">Delete reminders task</param>
        /// <param name="e">Delete reminders event args</param>
        private void DeleteRemindersTask_Completed(object sender, TaskCompletedEventArgs e)
        {
            Debug.Assert(sender != null, "sender != null");
            Debug.Assert(e != null, "e != null");
            Debug.Assert(e.Context != null, "e.Context != null");
            Debug.Assert(e.Context == Context, "e.Context == Context");

            Logger.WriteTraceVerbose(Context, "DeleteInvalidRemindersTask.DeleteRemindersTask_Completed");

            sender.AssertCast<DeleteRemindersTask>().Completed -= DeleteRemindersTask_Completed;

            if (e.Error == null)
            {
                Logger.WriteTraceInformation(Context, "Deleted invalid reminders.");
            }

            RaiseCompleted(e);
        }

        /// <summary>
        /// Gets the search expression for calendar items that have invalid reminders. 
        /// </summary>
        /// <remarks>
        /// Automatically added reminders can become invalid after the rules have been edited. This method creates a search expression
        /// that looks for calendar items that have automatically generated reminders from rules that are no longer present in the current
        /// set of rules. Each calendar item that has an automatically generated reminder stores the rule ID of the rule that generated 
        /// the reminder in an extended property. The client ensures that when rules are modified, the rule IDs get updated.
        /// </remarks>
        /// <returns>
        /// The search expression.
        /// </returns>
        private SearchExpressionType GetInvalidRulesSearchExpression()
        {
            OrType invalidRulesSearchExpression = new OrType();
            invalidRulesSearchExpression.Items = new SearchExpressionType[] { 
                GetInvalidRulesSearchExpression(ExchangeHelpers.GetExtendedPropertyPathForReminderRuleId()), 
                GetInvalidRulesSearchExpression(ExchangeHelpers.GetExtendedPropertyPathForJoinNowRuleId()) };

            return invalidRulesSearchExpression;
        }

        /// <summary>
        /// Gets the search expression for calendar items that have invalid reminders according to the specified property. 
        /// </summary>
        /// <param name="propertyPath">Path to rule ID extednded property</param>
        /// <returns>
        /// The search expression.
        /// </returns>
        private SearchExpressionType GetInvalidRulesSearchExpression(PathToExtendedFieldType propertyPath)
        {
            List<SearchExpressionType> searchExpressionList = new List<SearchExpressionType>();

            ExistsType exists = new ExistsType();
            exists.Item = propertyPath;
            searchExpressionList.Add(exists);

            if (_rules != null)
            {
                for (int i = 0; i < _rules.Count; i++)
                {
                    if (_rules[i].IsEnabled && _rules[i].MailboxType == MailboxType.CalendarItem)
                    {
                        ConstantValueType ruleIdConstant = new ConstantValueType();
                        ruleIdConstant.Value = _rules[i].Id.ToString("D");

                        IsEqualToType equals = new IsEqualToType();
                        equals.Item = propertyPath;
                        equals.FieldURIOrConstant = new FieldURIOrConstantType();
                        equals.FieldURIOrConstant.Item = ruleIdConstant;

                        NotType notRuleId = new NotType();
                        notRuleId.Item = equals;

                        searchExpressionList.Add(notRuleId);
                    }
                }
            }

            Debug.Assert(searchExpressionList.Count > 0, "searchExpressionList.Count > 0");

            SearchExpressionType invalidRemindersSearchExpression = null;

            if (searchExpressionList.Count == 1)
            {
                invalidRemindersSearchExpression = searchExpressionList[0];
            }
            else
            {
                AndType andInvalidRemindersSearchExpression = new AndType();
                andInvalidRemindersSearchExpression.Items = searchExpressionList.ToArray();
                invalidRemindersSearchExpression = andInvalidRemindersSearchExpression;
            }

            return invalidRemindersSearchExpression;
        }
    }
}
